﻿#include <ncsppt_export.h>
#include <ncspropmgr.h>
#include <ncspropedit.h>
#include <ncsproptree.h>
#include <QApplication>
#include <QDebug>

#include <QVBoxLayout>
#include <QDialog>
#include <QPushButton>

#include <stdio.h>
#include <memory>

// first we extend NcsPropMgr 
class NcsStringEnumProp; 
class ExPropMgr; 

/** Base class for any compound type.  */
class NcsCompoundProp : public NcsProp
{
public:
    struct PropDef
    {
        int type;
        QString label;
        QVariant default_value;
        NcsPropMgr::EditorFn editor_fn;
        NcsPropMgr::DecoratorFn decorator_fn;
    };

protected:
    friend class ExPropMgr; 
    explicit NcsCompoundProp(NcsPropMgr *manager, const QVector<PropDef>& subprop_def);
};

//
class ExPropMgr : public NcsPropMgr
{
public:
    explicit ExPropMgr(QObject *parent = 0);

    static int compoundTypeId() { return NcsPropMgr::PT_USER+ 2 ; } // this is a demo for compound type.

    using NcsPropMgr::setValue;
    virtual bool setValue(NcsProp *property, const QString& str) override; 
    virtual NcsProp* addProperty(int prop_type, const QString &label= QString(), 
            EditorFn fn_edit_creator = 0, 
            DecoratorFn decorator = 0) override; 
    
    virtual NcsCompoundProp* addCompoundProperty(const QString &label= QString(), 
        const QVector<NcsCompoundProp::PropDef>& subprop_def={}, 
            EditorFn fn_edit_creator = 0, 
            DecoratorFn decorator = 0) ; 


    virtual QString valueText(const NcsProp* prop) const override;
};


//--------------------------------------------------------------------------------------------------NcsCompoundProp

NcsCompoundProp::NcsCompoundProp(NcsPropMgr *mgr, const QVector<PropDef>& subprop_def) : NcsProp(mgr)
{
    this->setPropType(ExPropMgr::compoundTypeId()); 
    this->setVariantValue("");
}

//--------------------------------------------------------------------------------------------------ExPropMgr

inline ExPropMgr::ExPropMgr(QObject *parent): NcsPropMgr(parent)
{
    // add factory for String Enum
    this->setTypeEditorFactoryFn(stringEnumTypeId(), 
            [](NcsProp* prop) -> QWidget* 
	{ 
		NpComboBox* combo = new NcsStringEnumEdit(prop, nullptr);
		NcsStringEnumProp* prop_strenum = dynamic_cast<NcsStringEnumProp*>(prop);
		if (prop_strenum) {
			combo->setItemAndData(prop_strenum->optionLabels(), prop_strenum->optionData());
		}
       
        //combo->loadPropValue(prop);
		return combo; 
	}); 

    this->setTypeEditorFactoryFn(intEnumTypeId(), 
            [](NcsProp* prop) -> QWidget* 
	{ 
		NpComboBox* combo = new NcsIntEnumEdit(prop, nullptr);
		NcsIntEnumProp* prop_strenum = dynamic_cast<NcsIntEnumProp*>(prop);
		if (prop_strenum) {
			combo->setItemAndData(prop_strenum->optionLabels(), prop_strenum->optionData());
		}
		return combo; 
	}); 
}

bool ExPropMgr::setValue(NcsProp *prop, const QString& str) 
{
    //if (prop->propType() == stringEnumTypeId())
    //    return dynamic_cast<NcsStringEnumProp*>(prop)->setVariantValue(str); 

    return  NcsPropMgr::setValue(prop, str);
}


QString ExPropMgr:: valueText(const NcsProp* prop) const 
{
    if (prop->propType() == compoundTypeId())
    {
        QString value_text("Compound: "); 
        for (auto p : prop->subProperties())
        {
            value_text += QString("{%1}").arg(p->value().toString()) ;
        }
        return value_text; 
    }
   

    return NcsPropMgr::valueText(prop);
}

NcsProp* ExPropMgr::addProperty(int prop_type, const QString &label, EditorFn fn_edit_creator, DecoratorFn decorator) 
{
    if (prop_type == compoundTypeId())
    {
        qWarning("Property of compound type should be created with ExPropMgr::addCompoundProperty() interface"); 
        return nullptr;
    }
    else
        return NcsPropMgr::addProperty(prop_type, label, fn_edit_creator, decorator); 
}

NcsCompoundProp* ExPropMgr::addCompoundProperty(const QString &label, 
        const QVector<NcsCompoundProp::PropDef>& subprop_def, 
        EditorFn fn_edit_creator, DecoratorFn decorator)  
{
    NcsCompoundProp* cpd_prop = new NcsCompoundProp(this, subprop_def);
    cpd_prop->setLabel(label);

    if (fn_edit_creator)
        setPropFactoryFn(cpd_prop, fn_edit_creator);

    this->addPropertyToMgr(cpd_prop);

    // create sub-properties ( setVariantValue can be called in NcsPropMgr only);
    for (const auto& item :subprop_def)
    {
        NcsProp* sub_prop =  this->addProperty( item.type, item.label, item.editor_fn, item.decorator_fn);
        this->setVariantValue(sub_prop, item.default_value);  // note type checking!
        cpd_prop->addSubProperty(sub_prop);
    }
   
    this->setVariantValue(cpd_prop, cpd_prop->valueText()); 
    return cpd_prop;
}


//--------------------------------------------------------------------------------------------------

int ncsproptree_demo(int argc, char* argv[])
{
    QApplication a(argc, argv);
    printf("test code of ncspropmgr!\n"); 
    
    ExPropMgr ppmgr;

    // create property of integer and double value, and add it to root group. 
    NcsProp* p1 = ppmgr.addProperty(NcsPropMgr::intTypeId(), "int property");
    ppmgr.setValue(p1, 1024);
    p1->setAttrib("tooltip", "property of intetger"); 
    p1->setAttrib("valuetooltip", "any intetger is ok!"); 

    NcsProp* p2 = ppmgr.addProperty(NcsPropMgr::doubleTypeId(), "double property");
    ppmgr.setValue(p2, 3.5);
    p2->setAttrib("valuetooltip", "double precision number is ok!"); 

    // create property of string type, and add it to a new group. 
    NcsProp* prop_str = ppmgr.addProperty(NcsPropMgr::stringTypeId(), "string property");
    ppmgr.setValue(prop_str, "hello ncsproptree"); 
    NcsProp* group = ppmgr.addProperty(NcsPropMgr::groupTypeId(), "my group");
    NcsProp* int_prop = ppmgr.addProperty(NcsPropMgr::intTypeId(), "int_prop_2");
    group->addSubProperty(int_prop); 
    ppmgr.setValue(int_prop, 251);
   
    // string enum
    NcsProp* prop_enumstr = ppmgr.addProperty(ExPropMgr::stringEnumTypeId(), "1-string enum");
    dynamic_cast<NcsStringEnumProp*>(prop_enumstr)->setOptions({"utf-8", "gb2312", "ascii"});
    ppmgr.setValue(prop_enumstr, "gb2312x");
	group->addSubProperty(prop_enumstr);

    NcsProp* prop_enumstr2 = ppmgr.addProperty(ExPropMgr::stringEnumTypeId(), "2-string enum");
    dynamic_cast<NcsStringEnumProp*>(prop_enumstr2)->setOptions({"utf-8", "gb2312", "ascii"});
    ppmgr.setValue(prop_enumstr2, "gb2312");
	group->addSubProperty(prop_enumstr2);

    // create integer enum value
    //
    NcsProp* intenum_prop = ppmgr.addProperty(NcsPropMgr::intEnumTypeId(), QObject::tr(u8"曲线样式"));
	QStringList list1;
	list1 << QObject::tr(u8"实线") << QObject::tr(u8"划线") << QObject::tr(u8"点线") << QObject::tr(u8"点划线") << QObject::tr(u8"点点划线");
	//list1 << QObject::tr(u8"Solid Line") << QObject::tr(u8"Dash") << QObject::tr(u8"Dot") << QObject::tr(u8"Dash Dot") << QObject::tr(u8"Dash Dot Dot");
    dynamic_cast<NcsIntEnumProp*>(intenum_prop)->setOptions(list1); 
    intenum_prop->setValue(0);

    // 一个特殊的prop，用来做测试. 
    
    NcsProp* group_prop = ppmgr.addProperty(NcsPropMgr::groupTypeId(), "group_prop");
    NcsProp* subprop_1 = ppmgr.addProperty(NcsPropMgr::intTypeId(), "subprop-1");
    NcsProp* subprop_2 = ppmgr.addProperty(NcsPropMgr::stringTypeId(), "subprop-2");
    NcsProp* subprop_3 = ppmgr.addProperty(NcsPropMgr::doubleTypeId(), "subprop-3");
    NcsProp* subprop_4 = ppmgr.addProperty(ExPropMgr::boolTypeId(), "subprop-4");

    ppmgr.setValue(subprop_1, 3);
    ppmgr.setValue(subprop_2, "string");
    ppmgr.setValue(subprop_3, 3.35);
    ppmgr.setValue(subprop_4, true);

    group_prop->addSubProperty(subprop_1);
    group_prop->addSubProperty(subprop_2);
    group_prop->addSubProperty(subprop_3);
    group_prop->addSubProperty(subprop_4);

	// bool value
    NcsProp* bool_prop = ppmgr.addProperty(ExPropMgr::boolTypeId(), "bool prop");
    ppmgr.setValue(bool_prop, true);
	group->addSubProperty(prop_enumstr);

    NcsProp* slst_prop = ppmgr.addProperty(ExPropMgr::stringListTypeId(), "slst prop");
    ppmgr.setValue(slst_prop, QStringList{"s1", "s2"});
	group->addSubProperty(prop_enumstr);

    // demo of compound properties
    
    NcsCompoundProp* cpd_prop = ppmgr.addCompoundProperty("first compound prop",
            { {ExPropMgr::intTypeId(), "sub-int", QVariant(1), 0, 0},
            {ExPropMgr::doubleTypeId(), "sub-int", QVariant(2.0), 0, 0},
            {ExPropMgr::stringTypeId(), "sub-string", QVariant("sub-string"), 0, 0}},
            0, 0); 


    // demo of color properties
    NcsProp* color_prop = ppmgr.addProperty(NcsPropMgr::colorTypeId(), "color-prop-1"); 
    ppmgr.setValue(color_prop, QColor(128, 127, 126, 255)); 

    NcsProp* font_prop = ppmgr.addProperty(NcsPropMgr::fontTypeId(), "font-prop-1"); 
    ppmgr.setValue(font_prop, QApplication::font());

    // demo of stringdouble 

    NcsProp* double_prop= ppmgr.addProperty(NcsPropMgr::doubleTypeId(), "double-1");
    ppmgr.setValue(double_prop, 3.34); 

    NcsProp* double_prop2= ppmgr.addProperty(NcsPropMgr::doubleTypeId(), "double-2");
    ppmgr.setValue(double_prop2, "3.343"); 

    //bad value :: has null value
    NcsProp* null_prop = ppmgr.addProperty(NcsPropMgr::doubleTypeId(), "bad-double-value");
    //ppmgr.setValue(double_prop2, "3.343"); 

    // 以上创建的属性， 都采用默认编辑器， 以及默认的装饰器. 

    QDialog dlg;
    dlg.setLayout(new QVBoxLayout(&dlg)); 
    dlg.resize(600, 800);

    QHBoxLayout* hlay = new QHBoxLayout;
    QPushButton* btn1 = new QPushButton("button1", &dlg);
    QPushButton* btn2 = new QPushButton("one mgr multi tree", &dlg);
    QPushButton* btn3 = new QPushButton("reset mgr", &dlg);
    QPushButton* btn4 = new QPushButton("delete prop", &dlg);
    hlay->addWidget(btn1);
    hlay->addWidget(btn2);
    hlay->addWidget(btn3);
    hlay->addWidget(btn4);

    dlg.layout()->addItem(hlay);
    NcsPropTree* pptree  = new NcsPropTree (&dlg);
    dlg.layout()->addWidget(pptree);
    pptree->updatePropView(&ppmgr);
    //pptree->resize(600, 800);
    //pptree.show();   

    QObject::connect(btn1, &QPushButton::clicked, [&]() {
        QTreeWidgetItem* item = pptree->itemFromProperty(group);
        if (!item) return; 
        pptree->setBackgroundColor(item, QColor(120, 133, 153));
    });

    QObject::connect(btn2, &QPushButton::clicked, [&ppmgr]() {
        static int i = 0; 
        NcsPropTree *pptree2 = new NcsPropTree();
        pptree2->setWindowFlags(Qt::Popup | Qt::Dialog);
        pptree2->setAttribute(Qt::WA_DeleteOnClose);
        //pptree2->setWindowModality(Qt::ApplicationModal);
        pptree2->resize(300, 600);
        pptree2->move(0 + 300 * i++, 0);
        pptree2->updatePropView(&ppmgr);
        pptree2->show();
    });

    NcsPropMgr mgr2; 
    QObject::connect(btn3, &QPushButton::clicked, [&mgr2, pptree]() {
        pptree->clearPropTree();

        NcsProp* group_prop = mgr2.addProperty(NcsPropMgr::groupTypeId(), "group_prop");
        NcsProp* subprop_1 = mgr2.addProperty(NcsPropMgr::intTypeId(), "subprop-1");
        NcsProp* subprop_2 = mgr2.addProperty(NcsPropMgr::stringTypeId(), "subprop-2");
        NcsProp* subprop_3 = mgr2.addProperty(NcsPropMgr::doubleTypeId(), "subprop-3");
        NcsProp* subprop_4 = mgr2.addProperty(ExPropMgr::boolTypeId(), "subprop-4");
        NcsProp* subprop_5 = mgr2.addProperty(ExPropMgr::stringListTypeId(),"subprop-5");

        mgr2.setValue(subprop_1, 3);
        mgr2.setValue(subprop_2, "string");
        mgr2.setValue(subprop_3, 3.35);
        mgr2.setValue(subprop_4, true);
        mgr2.setValue(subprop_5,  QStringList{"list-1-1", "list-1-2"}); 

        group_prop->addSubProperty(subprop_1);
        group_prop->addSubProperty(subprop_2);
        group_prop->addSubProperty(subprop_3);
        group_prop->addSubProperty(subprop_4);
        group_prop->addSubProperty(subprop_5);

        pptree->updatePropView(&mgr2);
    });


    QObject::connect(btn4, &QPushButton::clicked, [&ppmgr, &p1, &group_prop]() {
        ppmgr.setDeleteBehavior(NcsPropMgr::MOVETOCACHE);
        ppmgr.removeProperty(p1);
        ppmgr.removeProperty(group_prop);
        p1 = nullptr;
        group_prop = nullptr; 
    });


    dlg.show();
    return a.exec(); 
}

int main_win32 (int argc, char* argv[]);

int main (int argc, char* argv[])
{

#ifdef MEMORY_LEAK_TEST_WIN32 
    return main_win32(argc, argv); 
#else
    return ncsproptree_demo(argc, argv); 
#endif
}

#ifdef MEMORY_LEAK_TEST_WIN32 

#ifndef NOMINMAX
#   define NOMINMAX // prevent windows redefining min/max
#endif

#ifndef WIN32_LEAN_AND_MEAN
#   define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>

// check if there is memory leaking. 
int main_win32(int argc, char* argv[])
{
    // pre def
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_REPORT_FLAG);
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);

    HANDLE hLogFile;

    {
        std::string memleak_file("libncsppt_memleak.log");
        hLogFile = CreateFileA(memleak_file.c_str(), GENERIC_WRITE,
            FILE_SHARE_WRITE | FILE_SHARE_READ,
            NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    }
    //_CrtSetBreakAlloc(6601);
    ncsproptree_demo(argc, argv) ; 

    // end memroy leak
    _CrtSetReportFile(_CRT_WARN, hLogFile);
    _CrtDumpMemoryLeaks();
    CloseHandle(hLogFile);
    return 0; 
}


#endif  //MEMORY_LEAK_TEST_WIN32
